package com.im.service.impl;

import com.im.config.Utils.GetHttpSessionConfigurator;
import com.im.domain.WebSocketBean;
import com.im.service.WebSocketServer;
import org.hibernate.validator.internal.util.logging.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基于javax webSocket通讯
 */
@ServerEndpoint(value = "/javax/websocket",configurator = GetHttpSessionConfigurator.class)
@Component
public class WebSocketServerImpl implements WebSocketServer {

    private Logger logger = LoggerFactory.getLogger(WebSocketServerImpl.class);

    /**
     * 错误最大重试次数
     */
    private static final int MAX_ERROR_NUM = 10;

    /**
     * 用来存放每个客户端对应的webSocket对象
     */
    private static Map<String,WebSocketBean> webSocketInfo;

    static{
        webSocketInfo = new ConcurrentHashMap<String,WebSocketBean>();
    }

    /**
     * 建立链接成功的回调方法
     *
     * @param session
     * @param config
     */
    @OnOpen
    @Override
    public void onOpen(Session session, EndpointConfig config) {
        // 如果是session没有激活的情况，就是没有请求获取session，这里可能会取空，需要实际业务处理
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        if(httpSession != null){
            logger.info("获取到session:"+httpSession.getId());
        }else{
            logger.error("未获取到httpSession");
        }
        // 链接成功当前对象放入webSocket对象集合
        WebSocketBean bean = new WebSocketBean();
        bean.setSession(session);
        webSocketInfo.put(session.getId(),bean);
        logger.info("客户端链接服务端session id:"+session.getId()+",当前链接数："+webSocketInfo.size());
    }

    /**
     * 断开链接方法
     *
     * @param session
     */
    @OnClose
    @Override
    public void onClose(Session session) {

        // 客户端断开链接移除webSocket对象
        webSocketInfo.remove(session.getId());
        logger.info("客户端断开链接，当前链接数："+webSocketInfo.size());
    }

    /**
     * 收到客户端消息后回调的方法
     *
     * @param session session对象
     * @param message 返回客户端的消息
     */
    @OnMessage
    @Override
    public void onMessage(Session session, String message) {

        logger.info("客户端session id:"+session.getId()+",消息："+message);
        // 此方法为客户端给服务器发送消息后进行的处理，可以根据业务自己处理，这里返回页面
        sendMessage(session,message);
    }

    /**
     * 发生异常是触发的方法
     *
     * @param session   session对象
     * @param throwable 抛出的异常
     */
    @OnError
    @Override
    public void onError(Session session, Throwable throwable) {

        logger.error("发生错误："+throwable.getMessage(),throwable);
    }

    /**
     * 向单个客户端发送消息
     *
     * @param session session对象
     * @param message 发送给客户端的消息
     */
    @Override
    public void sendMessage(Session session, String message) {

        try {
            //发送消息
            session.getBasicRemote().sendText(message);
            // 清空错误计数
            webSocketInfo.get(session.getId()).cleanErrorNum();
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("发送消息失败："+e.getMessage(),e);
            int errorLinkCount = webSocketInfo.get(session.getId()).getErrorLinkCount();
            // 小于最大重试次数
            if(errorLinkCount <= MAX_ERROR_NUM){
                // 重发
                sendMessage(session,message);
            }else{
                logger.error("发送消息失败超过最大次数");
                webSocketInfo.get(session.getId()).cleanErrorNum();
            }
        }
    }

    /**
     * 向所有在线客户群发消息
     *
     * @param message 发送个客户端的消息
     */
    @Override
    public void batchSendMessage(String message) {

        Set<Map.Entry<String, WebSocketBean>> set = webSocketInfo.entrySet();
        for (Map.Entry<String, WebSocketBean> map : set){
            sendMessage(map.getValue().getSession(),message);
        }
    }
}
